﻿using System;
using System.Data;
using System.Data.SqlClient;
using MongoDB.Bson;
using MongoDB.Driver;

namespace MongoSqlImporter
{
    class Program
    {
        private static SqlConnection _sqlServer;
        private static MongoServer _mongoServer;
        private static string[] _args;

        static int Main(string[] args)
        {
            _args = args;

            try
            {
                if (args.Length < 2)
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("Syntax: MongoSqlImporter <sql connection string> <mongo connection string>");
                    return 1;
                }

                var destMongoDatabase = SqlServer.Database;

                var sqlTables =
                    ExecuteQuery(
                        "SELECT TABLE_SCHEMA + '.[' + TABLE_NAME + ']' [Table] FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'");

                var tableCtr = 0;

                Console.WriteLine();
                var cursorLeft = Console.CursorLeft;
                var cursorTop = Console.CursorTop;

                var mongoDb = MongoServer.GetDatabase(destMongoDatabase);
                foreach (DataRow sqlTableRow in sqlTables.Rows)
                {
                    tableCtr++;
                    var tableName = sqlTableRow[0].ToString();

                    Console.SetCursorPosition(cursorLeft, cursorTop);
                    Console.WriteLine("[{0}/{1}] Importing {2}...".PadRight(80), tableCtr, sqlTables.Rows.Count,
                                      tableName);

                    var l = Console.CursorLeft;
                    var t = Console.CursorTop;

                    var tableSchema = ExecuteQuery(string.Format("SELECT TOP 0 * FROM {0}", tableName));

                    var mongoCollection = mongoDb.GetCollection<BsonDocument>(tableName);
                    var rowCtr = 0;
                    var rowCount = ExecuteScalar(string.Format("SELECT COUNT(1) FROM {0}", tableName));
                    using (var tableReader = ExecuteReader(string.Format("SELECT * FROM {0}", tableName)))
                    {
                        while (tableReader.Read())
                        {
                            var document = new BsonDocument();
                            foreach (DataColumn column in tableSchema.Columns)
                            {
                                var value = tableReader.GetValue(column.Ordinal);
                                switch (value.GetType().ToString())
                                {
                                    case "System.Decimal":
                                        value = tableReader.GetDouble(column.Ordinal);
                                        break;
                                    case "System.DBNull":
                                        value = BsonNull.Value;
                                        break;
                                }

                                BsonValue bsonValue;
                                document.Add(column.ColumnName,
                                             BsonTypeMapper.TryMapToBsonValue(value, out bsonValue)
                                                 ? bsonValue
                                                 : value.ToString());
                            }
                            mongoCollection.Insert(document);
                            rowCtr++;

                            Console.ForegroundColor = ConsoleColor.Green;
                            Console.SetCursorPosition(l, t);
                            Console.Write("{0} done.".PadRight(80), ((double)rowCtr / rowCount).ToString("0.00%"));
                            Console.ResetColor();
                        }
                    }
                }
                Console.WriteLine();
                Console.WriteLine("Finish.");
                return 0;
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("ERROR: {0}", ex.Message);
                return 1;
            }
            finally
            {
                Console.ResetColor();
            }
        }

        static SqlConnection SqlServer
        {
            get
            {
                if (_sqlServer == null)
                {
                    try
                    {
                        _sqlServer = new SqlConnection(_args[0]);
                        _sqlServer.Open();
                    }
                    catch (Exception)
                    {
                        throw new ApplicationException(
                            "SQL SERVER connection failed. \r\nPlease check if your connection string is correct and your database is online.");
                    }
                }
                return _sqlServer;
            }
        }
        static MongoServer MongoServer
        {
            get
            {
                try
                {
                    return _mongoServer ?? (_mongoServer = MongoServer.Create(_args[1]));
                }
                catch (Exception)
                {
                    throw new ApplicationException(
                        "MONGODB SERVER connection failed. \r\nPlease check if your connection string is correct and your database is online.");
                }
            }
        }

        static IDataReader ExecuteReader(string query)
        {
            var sqlCommand = new SqlCommand(query, SqlServer);
            return sqlCommand.ExecuteReader();
        }

        static DataTable ExecuteQuery(string query)
        {
            var dataTable = new DataTable();
            using (var sqlCommand = new SqlCommand(query, SqlServer))
            {
                var reader = sqlCommand.ExecuteReader();
                dataTable.Load(reader);
            }
            return dataTable;
        }

        static int ExecuteScalar(string query)
        {
            using (var sqlCommand = new SqlCommand(query, SqlServer))
            {
                return (int) sqlCommand.ExecuteScalar();
            }
        }
    }
}
